/*
 * clutilits.h
 *
 *  Created on: 2016年2月25日
 *      Author: guyadong
 */

#ifndef FACEDETECT_CL_UTILITS_H_
#define FACEDETECT_CL_UTILITS_H_
#include <iostream>
#include <type_traits>
#include <vector>
#include <stdexcept>
#include <unordered_map>
#include <sstream>
#include "mycl.h"
#include "assert_macros.h"
#include "file_utilits.h"
#include "string_utils.h"

namespace cl{

/* 将OpenCL C++对象数组转为对应的C对象数组 */
template<typename F,typename T=typename F::cl_type>
std::vector<T> cl_c_vector(const std::vector<F> &from){
    std::vector<T> v(from.size());
    for( auto i = from.size(); i >0; --i ) {
        v[i-1] = from[i-1]();
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::first数组 */
template<typename F1,typename F2 >
std::vector<F1> cl_c_vector1(const std::vector<std::pair<F1,F2>> &from){
	auto v=std::vector<F1>(from.size());
    for(auto i=from.size();i>0;--i){
    	v[i-1]=from[i-1].first;
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::first数组(char*) */
template<typename F2 >
std::vector<char*> cl_c_vector1(const std::vector<std::pair<std::string,F2>> &from){
	auto v=std::vector<char*>(from.size());
    for(auto i=from.size();i>0;--i){
    	v[i-1]=(char*)from[i-1].first.data();
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::second(OpenCL C++对象)数组并转为C对象数组 */
template<typename F1,typename F2 ,typename T=typename F2::cl_type>
std::vector<T> cl_c_vector2(const std::vector<std::pair<F1,F2>> &from){
	auto v=std::vector<T>(from.size());
    for(auto i=from.size();i>0;--i){
    	v[i-1]=from[i-1].second();
    }
    return std::move(v);
}
/* 根据向量元素类型和向量长度返回opencl向量类型，
 * 如 cl_vector_type<2,cl_int>::type 为 cl_int2
 */
template<int SIZE,typename T>
struct cl_vector_type{
	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_char>::value, cl_char2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_char>::value, cl_char4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_char>::value, cl_char8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_char>::value,cl_char16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_uchar>::value, cl_uchar2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_uchar>::value, cl_uchar4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_uchar>::value, cl_uchar8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_uchar>::value,cl_uchar16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_short>::value, cl_short2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_short>::value, cl_short4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_short>::value, cl_short8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_short>::value,cl_short16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_ushort>::value, cl_ushort2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_ushort>::value, cl_ushort4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_ushort>::value, cl_ushort8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_ushort>::value,cl_ushort16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_int>::value, cl_int2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_int>::value, cl_int4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_int>::value, cl_int8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_int>::value,cl_int16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_uint>::value, cl_uint2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_uint>::value, cl_uint4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_uint>::value, cl_uint8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_uint>::value,cl_uint16>::type vector_type(_T);
																														
	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_long>::value, cl_long2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_long>::value, cl_long4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_long>::value, cl_long8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_long>::value,cl_long16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_ulong>::value, cl_ulong2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_ulong>::value, cl_ulong4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_ulong>::value, cl_ulong8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_ulong>::value,cl_ulong16>::type vector_type(_T);
																														  
	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_float>::value, cl_float2>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_float>::value, cl_float4>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_float>::value, cl_float8>::type vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_float>::value,cl_float16>::type vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if< 2== _SIZE&&std::is_same<_T,cl_double>::value, cl_double2>::type  vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 4== _SIZE&&std::is_same<_T,cl_double>::value, cl_double4>::type  vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if< 8== _SIZE&&std::is_same<_T,cl_double>::value, cl_double8>::type  vector_type(_T);
	template<int _SIZE,typename _T>static typename std::enable_if<16== _SIZE&&std::is_same<_T,cl_double>::value,cl_double16>::type  vector_type(_T);

	template<int _SIZE,typename _T>static typename std::enable_if<2!= _SIZE && 4 != _SIZE && 8 != _SIZE &&16 != _SIZE>::type vector_type(...);
	using type=decltype(vector_type<SIZE>(std::declval<T>()));
};
/*
 * 根据opencl 向量类型返回向量的元素类型和向量长度，
 * 如is_cl_vector<cl_int2>::type 为 cl_int
 *  is_cl_vector<cl_int2>::value 为true 是opencl向量类型
 *  is_cl_vector<cl_int2>::size 为向量长度
 */
template<typename T>
struct is_cl_vector{
	template<typename _T, typename ENABLE=void>
	struct vector_size{
		enum{size=-1};
	};
	template<typename _T>
	struct vector_size<_T, typename std::enable_if<
		std::is_same<_T, cl_char2>::value
		|| std::is_same<_T, cl_uchar2>::value
		|| std::is_same<_T, cl_short2>::value
		|| std::is_same<_T, cl_ushort2>::value
		|| std::is_same<_T, cl_int2>::value
		|| std::is_same<_T, cl_uint2>::value
		|| std::is_same<_T, cl_long2>::value
		|| std::is_same<_T, cl_ulong2>::value
		|| std::is_same<_T, cl_float2>::value
		|| std::is_same<_T, cl_double2>::value
	>::type>{
		enum{size=2};
	};
	template<typename _T>
	struct vector_size<_T, typename std::enable_if<
		std::is_same<_T, cl_char4>::value
		|| std::is_same<_T, cl_uchar4>::value
		|| std::is_same<_T, cl_short4>::value
		|| std::is_same<_T, cl_ushort4>::value
		|| std::is_same<_T, cl_int4>::value
		|| std::is_same<_T, cl_uint4>::value
		|| std::is_same<_T, cl_long4>::value
		|| std::is_same<_T, cl_ulong4>::value
		|| std::is_same<_T, cl_float4>::value
		|| std::is_same<_T, cl_double4>::value
	>::type>{
		enum{size=4};
	};
	template<typename _T>
	struct vector_size<_T, typename std::enable_if<
		std::is_same<_T, cl_char8>::value
		|| std::is_same<_T, cl_uchar8>::value
		|| std::is_same<_T, cl_short8>::value
		|| std::is_same<_T, cl_ushort8>::value
		|| std::is_same<_T, cl_int8>::value
		|| std::is_same<_T, cl_uint8>::value
		|| std::is_same<_T, cl_long8>::value
		|| std::is_same<_T, cl_ulong8>::value
		|| std::is_same<_T, cl_float8>::value
		|| std::is_same<_T, cl_double8>::value
	>::type>{
		enum{size=8};
	};
	template<typename _T>
	struct vector_size<_T, typename std::enable_if<
		std::is_same<_T, cl_char16>::value
		|| std::is_same<_T, cl_uchar16>::value
		|| std::is_same<_T, cl_short16>::value
		|| std::is_same<_T, cl_ushort16>::value
		|| std::is_same<_T, cl_int16>::value
		|| std::is_same<_T, cl_uint16>::value
		|| std::is_same<_T, cl_long16>::value
		|| std::is_same<_T, cl_ulong16>::value
		|| std::is_same<_T, cl_float16>::value
		|| std::is_same<_T, cl_double16>::value
	>::type>{
		enum{size=16};
	};

	static cl_char check(cl_char2);
	static cl_char check(cl_char4);
	static cl_char check(cl_char8);
	static cl_char check(cl_char16);

	static cl_uchar check(cl_uchar2);
	static cl_uchar check(cl_uchar4);
	static cl_uchar check(cl_uchar8);
	static cl_uchar check(cl_uchar16);

	static cl_short check(cl_short2);
	static cl_short check(cl_short4);
	static cl_short check(cl_short8);
	static cl_short check(cl_short16);

	static cl_ushort check(cl_ushort2);
	static cl_ushort check(cl_ushort4);
	static cl_ushort check(cl_ushort8);
	static cl_ushort check(cl_ushort16);

	static cl_int check(cl_int2);
	static cl_int check(cl_int4);
	static cl_int check(cl_int8);
	static cl_int check(cl_int16);

	static cl_uint check(cl_uint2);
	static cl_uint check(cl_uint4);
	static cl_uint check(cl_uint8);
	static cl_uint check(cl_uint16);

	static cl_long check(cl_long2);
	static cl_long check(cl_long4);
	static cl_long check(cl_long8);
	static cl_long check(cl_long16);

	static cl_ulong check(cl_ulong2);
	static cl_ulong check(cl_ulong4);
	static cl_ulong check(cl_ulong8);
	static cl_ulong check(cl_ulong16);

	static cl_float check(cl_float2);
	static cl_float check(cl_float4);
	static cl_float check(cl_float8);
	static cl_float check(cl_float16);

	static cl_double check(cl_double2);
	static cl_double check(cl_double4);
	static cl_double check(cl_double8);
	static cl_double check(cl_double16);
	static void check(...);
	using type=decltype(check(std::declval<T>()));
	enum{value=!std::is_void<type>::value,size=vector_size<T>::size};
};

template<typename T,typename C=float
		,typename RET=typename std::enable_if<std::is_arithmetic<T>::value,typename std::conditional<std::is_floating_point<T>::value,T,C>::type>::type>
inline
RET
length(const T &x,const T &y){
	return RET(std::sqrt(std::pow(RET(x),2)+std::pow(RET(y),2)));
}
/*
 * 递归计算向量所有元素的平方和（递归结束）
 * */
template<typename T,typename C=float,typename VI=is_cl_vector<T>
		,typename RET=typename std::conditional<std::is_floating_point<typename VI::type>::value,T,C>::type>
inline
typename std::enable_if<2==VI::size&&VI::value,RET>::type
square_sum(const T &pos){
	return RET(std::pow(RET(pos.s[0]),2)+std::pow(RET(pos.s[1]),2));
}
/*
 * 递归计算向量所有元素的平方和
 * */
template<typename T,typename C=float,typename VI=is_cl_vector<T>
	,typename RET=typename std::conditional<std::is_floating_point<typename VI::type>::value,T,C>::type>
inline
typename std::enable_if<2<VI::size&&VI::value,RET>::type
square_sum(const T &pos){
	return square_sum(pos.hi)+square_sum(pos.lo);
}
template<typename T,typename C=float,typename VI=is_cl_vector<T>
		,typename RET=typename std::enable_if<VI::value,typename std::conditional<std::is_floating_point<typename VI::type>::value,T,C>::type>::type>
inline
RET
length(const T &pos){
	return RET(std::sqrt(square_sum(pos)));
}

template<typename T,typename C=float,typename VI = cl::is_cl_vector<T>
		,typename RET=typename std::enable_if<VI::value,typename std::conditional<std::is_floating_point<typename VI::type>::value,T,C>::type>::type>
inline
RET
distance(const T &p0,const T &p1){
	return length(p0-p1);
}

} /* namespace cl */
////////////////begin global namespace//////////////
/*
 * (递归结束)向量减法操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET= typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2 == VI::size, RET>::type
operator-(const T &p1, const T &p2) {
	return{ p1.s[0] - p2.s[0],p1.s[1] - p2.s[1] };
}
/*
 * (递归)向量减法操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2<VI::size, RET>::type
operator-(const T &p1, const T &p2) {
	RET r;
	r.hi = p1.hi - p2.hi;
	r.lo = p1.lo - p2.lo;
	return r;
}
/*
 * (递归结束)向量减法操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET= typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2 == VI::size, RET>::type
operator-(const T &p1, const typename VI::type &p2) {
	return{ p1.s[0] - p2,p1.s[1] - p2};
}
/*
 * (递归)向量减法操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2<VI::size, RET>::type
operator-(const T &p1, const typename VI::type &p2) {
	RET r;
	r.hi = p1.hi - p2;
	r.lo = p1.lo - p2;
	return r;
}
/*
* (递归结束)向量减法操作符,第一个操作数非向量
*/
template<typename N, typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value&&std::is_same<N, typename VI::type>::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
	inline
	typename std::enable_if<2==VI::size, RET>::type
	operator-(const N &p1, const  T&p2) {
	return{ p1 - p2.s[0],p1 - p2.s[1] };
}

/*
 * (递归)向量减法操作符,第一个操作数非向量
 */
template<typename N,typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value&&std::is_same<N, typename VI::type>::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] - std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2<VI::size, RET>::type
operator-(const N &p1, const  T&p2) {
	RET r;
	r.hi = p1 - p2.hi;
	r.lo = p1 - p2.hi;
	return r;
}
/*
 * (递归结束)向量加法操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET= typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] + std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2 == VI::size, RET>::type
operator+(const T &p1, const T &p2) {
	return{ p1.s[0] + p2.s[0],p1.s[1] + p2.s[1] };
}
/*
 * (递归)向量加法操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] + std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2<VI::size, RET>::type
operator+(const T &p1, const T &p2) {
	RET r;
	r.hi = p1.hi + p2.hi;
	r.lo = p1.lo + p2.lo;
	return r;
}
/*
 * (递归结束)向量加法操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	,typename RET=typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] + std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2 == VI::size, RET>::type
operator+(const T &p1, const typename VI::type &p2) {
	return { p1.s[0] + p2,p1.s[1] + p2 };
}
/*
 * (递归)向量加法操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] + std::declval<T>().s[0])>::type>
inline
typename std::enable_if<2<VI::size, RET>::type
operator+(const T &p1, const typename VI::type &p2) {
	RET r;
	r.hi = p1.hi + p2;
	r.lo = p1.lo + p2;
	return r;
}

/*
 * 向量加法操作符,第一个操作数非向量
 */
template<typename N,typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value&&std::is_same<N, typename VI::type>::value>::type
	, typename RET = typename cl::cl_vector_type<VI::size, decltype(std::declval<T>().s[0] + std::declval<T>().s[0])>::type>
inline
RET
operator+(const N &p1, const  T&p2) {
	return p2+p1;
}

/*
 * (递归结束)向量==操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2 == VI::size, bool>::type
operator==(const T &p1, const T &p2) {	return  p1.s[0] == p2.s[0]&&p1.s[1] == p2.s[1] ;}
/*
 * (递归)向量==操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2<VI::size, bool>::type
operator==(const T &p1, const T &p2) {	return p1.hi == p2.hi&&p1.lo == p2.lo;}
/*
 * (递归结束)向量==操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2 == VI::size, bool>::type
operator==(const T &p1, const typename VI::type &p2) {return  p1.s[0] == p2&&p1.s[1] == p2 ;}
/*
 * (递归)向量==操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2<VI::size, bool>::type
operator==(const T &p1, const typename VI::type &p2) {	return p1.hi == p2&&p1.lo == p2;}
/*
 * 向量==操作符,第一个操作数非向量
 */
template<typename N,typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value&&std::is_same<N, typename VI::type>::value>::type>
inline bool operator==(const N &p1, const  T&p2) {return p2==p1;}

/*
 * (递归结束)向量!=操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2 == VI::size, bool>::type
operator!=(const T &p1, const T &p2) {	return  p1.s[0] != p2.s[0]||p1.s[1] != p2.s[1] ;}
/*
 * (递归)向量!=操作符
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2<VI::size, bool>::type
operator!=(const T &p1, const T &p2) {	return p1.hi != p2.hi||p1.lo != p2.lo;}
/*
 * (递归结束)向量!=操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2 == VI::size, bool>::type
operator!=(const T &p1, const typename VI::type &p2) {return  p1.s[0] != p2||p1.s[1] != p2 ;}
/*
 * (递归)向量!=操作符,第二个操作数非向量
 */
template<typename T, typename VI = cl::is_cl_vector<T>, typename ENABLE = typename std::enable_if<VI::value>::type>
inline
typename std::enable_if<2<VI::size, bool>::type
operator!=(const T &p1, const typename VI::type &p2) {	return p1.hi != p2||p1.lo != p2;}
/*
 * 向量!=操作符,第一个操作数非向量
 */
template<typename N,typename T, typename VI = cl::is_cl_vector<T>
	, typename ENABLE = typename std::enable_if<VI::value&&std::is_same<N, typename VI::type>::value>::type>
inline bool operator!=(const N &p1, const  T&p2) {return p2!=p1;}

////////////////end global namespace//////////////


namespace gdface {
	using build_log_type=std::vector<std::pair<cl::Device, typename cl::detail::param_traits<cl::detail::cl_program_build_info, CL_PROGRAM_BUILD_LOG>::param_type>>;
	// 内核源码描述类 pair.first为源码名字，pair.second为源码对象(cl::Progam)
	using program_info_type =std::pair<std::string,cl::Program>;

class ProgramExt:public cl::Program{
public:
	using cl::Program::Program;
#if defined(CL_VERSION_1_2)
    cl_int compile(
    	const std::vector<cl::Device> &devices,
    	const std::vector<program_info_type> &input_headers,
        const char* options = NULL,
        void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
        void* data = NULL) const;
#endif
};
std::string load_program_string(const std::string &path);
class cl_utilits {
public:
	static std::string deviceTypeToString(cl_device_type type);
	static std::string imageChannelOrderToString(cl_channel_order channelOrder);

	static std::string imageChannelDataTypeToString(cl_channel_type channelDataType);
	static std::string imageFormatToString(const cl::ImageFormat& format);
	static std::string memFlagsToString(cl_mem_flags flags);
	static std::string memObjectTypeToString(cl_mem_object_type type);
	static std::string errorNumberToString(cl_int errorNumber);
	static void showClError(const cl::Error& e, std::ostream& stream = std::cout);
	static cl::ImageFormat getFittedImageFormatForGray(const cl::Context& context,
			cl_mem_flags flags = CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR);
	static void showDevice(const cl::Device& device, std::ostream& stream = std::cout);
	static void showDevices(const std::vector<cl::Device>& devices, std::ostream& stream = std::cout);
	static void showSupportedImageFormats(const cl::Context& context, cl_mem_flags flags, cl_mem_object_type type =
			CL_MEM_OBJECT_IMAGE2D, std::ostream& stream = std::cout);
	static std::vector<uint8_t> mem_download(const cl::Memory& memobj);
	static std::vector<uint8_t> image_download(const cl::Image& img);
    /**
     * Build info function that returns a vector of device/info pairs for the specified
     * info type and for all devices in the program.
     * On an error reading the info for any device, an empty vector of info will be returned.
     */
    template <cl_int name,typename param_type =typename cl::detail::param_traits<cl::detail::cl_program_build_info, name>::param_type >
    static std::vector<std::pair<cl::Device, param_type>>
        getBuildInfo(const cl::Program &program)
    {
        auto devs = program.getInfo<CL_PROGRAM_DEVICES>();
        std::vector<std::pair<cl::Device, param_type>>
            devInfo;

        for (cl::Device d : devs) {
            auto param=  program.getBuildInfo<name>(d);
            devInfo.push_back(
                std::pair<cl::Device, param_type>
                (d, param));
       }
        return std::move(devInfo);
    }
	static void showBuildLog(build_log_type& build_log,const std::string &source_name=Unknow_Name, std::ostream& stream = std::cout);
	/* 内核程序编译参数类 出错抛出std::invalid_argument异常 */
	struct build_param{
		// 内核源码描述类 pair.first为源码名字，pairt.second为源码
		using source_info_type =std::pair<std::string,std::string>;
		cl::Context context; // 设备上下文
		std::vector<cl::Device> devices; // 指定编译的目标设备列表
		std::vector<source_info_type> sources; // 要编译的源码清单
		std::vector<program_info_type> input_headers; // 指定include对象
		std::string compiler_options; // 所有编译选项
		std::string linker_options; // 所有连接选项
		build_param(const cl::Context &context,
			const std::vector<cl::Device> &devices,
			const std::vector<source_info_type> &sources,
			const std::vector<program_info_type> &input_headers,
			const std::string compiler_options,
			const std::string linker_options):context(context),
					devices(devices),
					sources(sources),
					input_headers(input_headers),
					compiler_options(compiler_options),
					linker_options(linker_options){}

		class builder{
		private:
			const cl::Context _context;
			std::vector<cl::Device> _devices;
			std::vector<source_info_type> _sources;
			std::string _source_root=Empty_String;
			std::vector<std::string> _source_files;
			std::string _compiler_options=Empty_String;
			std::string _linker_options=Empty_String;
			std::vector<program_info_type> _input_headers;
		public:
			// builder 构造函数
			builder(const cl::Context &context) :_context(context) {}
			// 设置_devices参数，提供目标设备列表清单
			builder &set_devices(std::initializer_list<cl::Device>devices){
				this->_devices=devices;
				return *this;
			}
			// 设置_devices参数，提供目标设备列表清单
			builder &set_devices(const std::vector<cl::Device>&devices){
				this->_devices=devices;
				return *this;
			}
			// 设置添加一组源码对象
			builder &add_sources(std::initializer_list<source_info_type>sources){
				this->_sources.insert(this->_sources.end(),sources);
				return *this;
			}
			// 设置添加一个源码对象
			builder &add_source(const source_info_type &source){
				return add_sources({source});
			}
			// 设置添加一个源码对象，source_name可以不提供
			builder &add_source(const std::string &source,const std::string &source_name=Unknow_Name){
				return add_source({source,source_name});
			}
			/* 指定所有源码的根路径
			 * 同时调用add_include将根路径指定为#include搜索路径
			 * */
			builder &set_source_root(const std::string &root){
				if(_source_root.empty()&&!root.empty()){
					_source_root=root;
					add_include(root);
				}
				return *this;
			}
			/* 加入一组source源码文件 */
			builder &add_source_files(std::initializer_list<std::string>sources){
				this->_source_files.insert(this->_source_files.end(),sources);
				return *this;
			}
			/* 加入一个source源码文件 */
			builder &add_source_file(const std::string &source) {
				return add_source_files({source});
			}
			// 设置添加一组include cl::Program对象
			builder &add_input_headers(std::initializer_list<program_info_type> programs) {
				this->_input_headers.insert(this->_input_headers.begin(), programs);
				return *this;
			}
			// 设置添加一组include cl::Program对象
			builder &add_input_headers(const std::vector<program_info_type> &programs) {
				this->_input_headers.insert(this->_input_headers.begin(), programs.begin(),programs.end());
				return *this;
			}
			// 设置添加一组include源码对象
			builder &add_input_headers(std::initializer_list<source_info_type> headers){
				for(auto p:headers){
					this->_input_headers.emplace_back(p.first,cl::Program(_context,p.second.data()));
				}
				return *this;
			}
			// 设置添加一组include源码文件
			builder &add_input_header_files(std::vector<const char*> files,const std::string &root = "") {
				for (decltype(files.size()) i=0;i<files.size();++i) {
					auto file= std::string(files[i]);
					throw_if_msg(file.empty(), "the argument 'file' is empty")
					try{
						this->_input_headers.emplace_back(file, cl::Program(_context,load_program_string(gdface::path_concate(root, file).data())));
					}catch (std::exception &e) {
						throw std::invalid_argument(std::string(SOURCE_AT).append(":").append(e.what()));
					}
				}
				return *this;
			}

			/* 添加预定义宏 -D */
			builder &add_define(const std::string &def){
				throw_if_msg(def.empty(),"def is empty")
				_compiler_options+="-D "+def+" ";
				return *this;
			}
			template<typename T>
			builder &add_define(const std::string &def,const T &v){
				throw_if_msg(def.empty(),"def is empty")
				std::stringstream stream;
				stream<<def<<"="<<v;
				_compiler_options+="-D "+stream.str()+" ";
				return *this;
			}
			/* 添加头文件搜索路径 -I */
			builder &add_include(const std::string &dir){
				auto p=gdface::trim(dir);
				throw_if_msg(p.empty(),"dir is empty")
				if(gdface::has_space(p))
					p="\""+p+"\"";
				_compiler_options+="-I "+p+" ";
				return *this;
			}
			/* 添加通用编译选项 */
			builder &add_compiler_options(const std::string &opt){
				if(!opt.empty())
					_compiler_options+=opt+" ";
				return *this;
			}
			/* 添加通用编译选项 */
			builder &add_linker_options(const std::string &opt){
				if(!opt.empty())
					_linker_options+=opt+" ";
				return *this;
			}
			/* 生成build_param对象 */
			build_param build(){
				if(_source_root.empty()){
					add_include(gdface::getcwd()); //将当前路径加入#include文件搜索路径
				}
				// 将源码文件转为std::string
				for(auto file:_source_files){
					throw_if_msg(file.empty(),"the argument 'file' is empty")
					try {
						this->_sources.emplace_back(file, load_program_string(gdface::path_concate(_source_root, file).data()));
					}catch (std::exception &e) {
						throw std::invalid_argument(std::string(SOURCE_AT).append(":").append(e.what()));
					}
				}
				// 当OpenCL设备只有1个时，定义CL_DEVICE_LOCAL_MEM_SIZE
				if (1 == _devices.size()) {
					throw_if_msg(CL_NONE == _devices[0].getInfo<CL_DEVICE_LOCAL_MEM_TYPE>(), 
						_devices[0].getInfo<CL_DEVICE_NAME>().append(" not support local memory"))					
					add_define("CL_DEVICE_LOCAL_MEM_SIZE", _devices[0].getInfo<CL_DEVICE_LOCAL_MEM_SIZE>());
				}
				// 返回build_param对象
				return {_context,_devices,_sources,_input_headers,_compiler_options,_linker_options };
			}
		};
		/* 静态成员函数 根据cl::Context构造builder对象 */
		static builder custom(const cl::Context &context){
			return builder(context);
		}
		// 允许缺省构造
		build_param() = default;
		// 允许复制构造
		build_param(const build_param &)=default;
		// 允许移动构造
		build_param(build_param &&)=default;
		// 允许赋值操作符
		build_param&operator=(const build_param &)=default;
		std::string build_options()const { return compiler_options + linker_options; }
	};
	static  cl::Program linkProgram(
		std::vector<cl::Program> inputPrograms,
		const char* options = NULL,
		void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
		void* data = NULL,
		cl_int* err = NULL);
	enum build_status_type{
		build,complie,link
	};
	static void show_on_buildstart(const std::string &type,const std::string&options,const build_param::source_info_type &source,std::ostream &stream=std::cout){
#ifndef NDEBUG
		stream<<type<<": "<<source.first<<std::endl;
		if(!options.empty())
			stream<<"options:"<<options<<std::endl;
#endif
	}
	static void show_on_buildend(const cl::Program &program,const build_param::source_info_type &source){
#ifndef NDEBUG
			auto log = cl_utilits::getBuildInfo<CL_PROGRAM_BUILD_LOG>(program);
			showBuildLog(log, source.first);
#endif
	}
	static cl::Program buildSource(const build_param& param, bool complie_only = false);

#ifdef CL_VERSION_1_2
	static cl::Program compileSource(const build_param& param);
	static cl::Program buildMultiFilesProgram(const build_param& param);
#endif
	static cl::Program buildExecutableProgram(const build_param& param);

	using kernel_table_type=std::unordered_map<std::string, cl::Kernel>;

	static cl_utilits::kernel_table_type createKernels(const build_param& param);

	static cl::Buffer createBuffer(cl_mem_flags flags,const cl::Context &context,size_t size,const void* ptr);

	static cl::Image2D createImage2D(cl_mem_flags flags,const cl::Context &context,
			const cl::ImageFormat& format,
			size_t width,
			size_t height,
			size_t row_pitch=0,
			const void* ptr=nullptr);
	/* 设备是否支持double类型 */
	static bool double_supported(cl::Device device) {
		return  ( (CL_FP_FMA | CL_FP_ROUND_TO_NEAREST | CL_FP_ROUND_TO_ZERO | CL_FP_ROUND_TO_INF | CL_FP_INF_NAN | CL_FP_DENORM)
			== device.getInfo<CL_DEVICE_DOUBLE_FP_CONFIG>());
	}
	static std::pair<size_t, size_t> kernel_preferred_work_group_size_multiple(const cl::Device& device);
	static const std::string Unknow_Name;
	static const std::string Empty_String;
};
#define PRINT_CL_ERROR(e,stream) stream<<SOURCE_AT<<":"<<e.what()<<":"<<gdface::cl_utilits::errorNumberToString(e.err())<<std::endl
#define PRINT_CL_ERROR_COUT(e) PRINT_CL_ERROR(e,std::cout)

/* face异常类 */
class face_exception:public std::logic_error{
public:
	// 继承基类构造函数
	using std::logic_error::logic_error;
	face_exception(const std::string &at, const std::string &msg):logic_error(std::string(at).append(":").append(msg)){}
	face_exception(const std::string &at, const std::exception&e):logic_error(std::string(at).append(":").append(e.what())){}
	face_exception(const std::string &at, const std::exception&e,const std::string &msg):logic_error(std::string(at).append(":").append(e.what()).append(":").append(msg)){}

};
/* face异常类 */
class face_cl_exception:public face_exception{
public:
	// 继承基类构造函数
	using face_exception::face_exception;
	face_cl_exception(const std::string &at,const cl::Error& e,const std::string &msg=""):
		face_exception(std::string(at).append(":").append(e.what())
				.append(":")
				.append(gdface::cl_utilits::errorNumberToString(e.err()))
				.append(":").append(msg)
				){}
};
/**
* Exception class for build errors to carry build info
*/
class face_cl_build_exception : public face_cl_exception
{
private:
	build_log_type buildLogs;
public:
    // 继承基类构造函数
    using face_cl_exception::face_cl_exception;
    face_cl_build_exception(const std::string &at,const cl::Error &e, const build_log_type &vec) : face_cl_exception(at,e){
    	buildLogs=vec;
    }
#if defined(CL_VERSION_2_0) && defined(CL_HPP_ENABLE_EXCEPTIONS) // 判断 #include <CL/cl2.hpp>
    face_cl_build_exception(const std::string &at,const cl::BuildError &e) : face_cl_build_exception(at,e,e.getBuildLog()){ }
#endif
    build_log_type getBuildLog() const{
        return buildLogs;
    }
};



} /* namespace gdface */



#endif /* FACEDETECT_CL_UTILITS_H_ */
